﻿#region 참조 네임스페이스

using System;
using System.Runtime.InteropServices;
using System.Text;

#endregion

namespace Skying.Windows.API
{
	/// <summary>
	/// Windows 라이브러리 파일 중 user32.dll에 대한 메서드를 제공한다.
	/// </summary>
	public class User32
	{
		#region FindWindow

		/// <summary>
		/// 클래스명과 윈도우명이 일치하는 최상위 윈도우의 핸들을 찾는다.
		/// </summary>
		/// <param name="className">클래스명을 설정한다.</param>
		/// <param name="windowName">윈도우명을 설정한다.</param>
		/// <returns>클래스명과 윈도우명이 일치하는 최상위 윈도우의 핸들이 반환된다.</returns>
		[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
		public static extern IntPtr FindWindow(string className, string windowName);

		#endregion

		#region ShowWindow

		/// <summary>
		/// 윈도우의 보여지는 상태를 설정한다.
		/// </summary>
		/// <param name="handle">보여지는 상태를 조정할 윈도우의 핸들을 지정한다.</param>
		/// <param name="command">대상 윈도우의 보여지는 상태를 어떻게 할 지 결정한다.</param>
		/// <returns>윈도우가 이전에 보여지고 있었으면 true, 그렇지 않으면 false가 반환된다.</returns>
		[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
		public static extern bool ShowWindow(IntPtr handle, CmdShow command);

		/// <summary>
		/// <see cref="Skying.Windows.API.User32.ShowWindow"/>를 통해 조정될 윈도우의 보여주기 상태를 열거한다.
		/// </summary>
		public enum CmdShow : int
		{
			/// <summary>
			/// <para>윈도우가 소유하는 스레드가 응답을 하지 않더라도 윈도우를 최소화시킨다.</para>
			/// <para>이 플래그는 다른 스레드로부터 윈도우를 최소화할 때 사용되어져야 한다.</para>
			/// </summary>
			SW_FORCEMINIMIZE = 11,

			/// <summary>
			/// 윈도우를 감추고 다른 윈도우를 활성화시킨다.
			/// </summary>
			SW_HIDE = 0,

			/// <summary>
			/// 윈도우를 최대화시킨다.
			/// </summary>
			SW_MAXIMIZE = 3,

			/// <summary>
			/// 윈도우를 최소화시키고, 다음 최상위 레벨의 윈도우를 활성화시킨다.
			/// </summary>
			SW_MINIMIZE = 6,

			/// <summary>
			/// <para>윈도우를 활성화시키고 화면에 표시한다. 만약 해당 윈도우가 최소화 혹은 최대화되어 있으면, 시스템은 그것을 원래 사이즈와 위치로 되돌린다.</para>
			/// <para>애플리케이션이 최소화된 윈도우를 되돌리고자 할 때는 이 플래그를 사용해야만 한다.</para>
			/// </summary>
			SW_RESTORE = 9,

			/// <summary>
			/// 윈도우를 활성화시키고 그것의 현재 위치와 크기로 표시한다.
			/// </summary>
			SW_SHOW = 5,

			/// <summary>
			/// 윈도우가 CreateProcess 메서드에 의해 실행될 때 전달받는 STARTUPINFO 구조체에 정의된 CmdShow 값에 기초하여 윈도우의 상태를 변경한다.
			/// </summary>
			SW_SHOWDEFAULT = 10,

			/// <summary>
			/// 윈도우를 활성화하고 최대화시킨다.
			/// </summary>
			SW_SHOWMAXIMIZED = 3,

			/// <summary>
			/// 윈도우를 활성화하고 최소화시킨다.
			/// </summary>
			SW_SHOWMINIMIZED = 2,

			/// <summary>
			/// 윈도우를 최소화된 상태로 표시한다. 이 플래그는 윈도우가 활성화되지 않는다는 점을 제외하고 <see cref="Skying.Windows.API.User32.CmdShow.SW_SHOWMINIMIZED"/> 플래그와 유사하다.
			/// </summary>
			SW_SHOWMINNOACTIVE = 7,

			/// <summary>
			/// 윈도우를 현재 크기와 위치로 표시한다. 이 플래그는 윈도우가 활성화되지 않는다는 점을 제외하고 <see cref="Skying.Windows.API.User32.CmdShow.SW_SHOW"/> 플래그와 유사하다.
			/// </summary>
			SW_SHOWNA = 8,

			/// <summary>
			/// 윈도우를 가장 최근의 크기와 위치로 표시한다. 이 플래그는 윈도우가 활성화되지 않는다는 점을 제외하고 <see cref="Skying.Windows.API.User32.CmdShow.SW_SHOWNORMAL"/> 플래그와 유사하다.
			/// </summary>
			SW_SHOWNOACTIVATE = 4,

			/// <summary>
			/// <para>윈도우를 활성화하고 표시한다. 윈도우가 최소화 혹은 최대화되어 있다면 시스템은 윈도우를 본래 크기와 위치로 되돌린다.</para>
			/// <para>애플리케이션은 윈도우가 제일 처음에 표시될 때 이 플래그를 지정해야 한다.</para>
			/// </summary>
			SW_SHOWNORMAL = 1
		}

		#endregion

		#region LoadBitmap

		/// <summary>
		/// 모듈의 실행 파일로부터 특정 비트맵 리소스를 로드한다.
		/// </summary>
		/// <param name="module">로드하려는 비트맵 리소스가 포함되어 있는 실행 파일의 모듈 핸들을 설정한다.</param>
		/// <param name="name">로드하려는 비트맵 리소스의 이름(null 문자가 포함되어 있는)의 포인터를 설정한다.</param>
		/// <returns>
		/// <para>함수가 성공할 경우, 비트맵 리소스에 대한 핸들이 반환된다.</para>
		/// <para>실패할 경우, null이 반환된다.</para>
		/// </returns>
		[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
		public static extern IntPtr LoadBitmap(IntPtr module, IntPtr name);

		#endregion

		#region LoadString

		/// <summary>
		/// 실행 파일로부터 문자열 리소스를 로드하여 버퍼로 내보낸다.(Unicode)
		/// </summary>
		/// <param name="module">문자열 리소스가 포함되어 있는 실행 파일의 모듈 인스턴스의 핸들을 설정한다.</param>
		/// <param name="id">로드하려는 문자열 리소스의 아이디를 설정한다.</param>
		/// <param name="buffer">문자열을 받기 위한 버퍼를 설정한다.</param>
		/// <param name="bufferMax">버퍼의 크기를 설정한다. 이 파라메터가 0일 경우, 버퍼는 리소스에 대한 읽기 전용 포인터를 전달받게 된다.</param>
		/// <returns>함수가 성공할 경우, 버퍼에 복사된 null 문자를 포함하지 않는 char의 개수가 반환된다. 문자열 리소스가 존재하지 않을 경우 0이 반환된다.</returns>
		[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
		public static extern int LoadStringW(IntPtr module, uint id, out StringBuilder buffer, int bufferMax);

		/// <summary>
		/// 실행 파일로부터 문자열 리소스를 로드하여 버퍼로 내보낸다.(ANSI)
		/// </summary>
		/// <param name="module">문자열 리소스가 포함되어 있는 실행 파일의 모듈 인스턴스의 핸들을 설정한다.</param>
		/// <param name="id">로드하려는 문자열 리소스의 아이디를 설정한다.</param>
		/// <param name="buffer">문자열을 받기 위한 버퍼를 설정한다.</param>
		/// <param name="bufferMax">버퍼의 크기를 설정한다. 이 파라메터가 0일 경우, 버퍼는 리소스에 대한 읽기 전용 포인터를 전달받게 된다.</param>
		/// <returns>함수가 성공할 경우, 버퍼에 복사된 null 문자를 포함하지 않는 char의 개수가 반환된다. 문자열 리소스가 존재하지 않을 경우 0이 반환된다.</returns>
		[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
		public static extern int LoadStringA(IntPtr module, uint id, out StringBuilder buffer, int bufferMax);

		#endregion

		#region Windows Hook

		/// <summary>
		/// 현재 후크 체인 내에서 다음 후크 프로시져로 후크 정보를 전달한다. 후크 프로시져는 후크 정보를 처리하기 전이나 후에 이 함수를 호출할 수 있다.
		/// </summary>
		/// <param name="hhk">이 파라메터는 무시된다.</param>
		/// <param name="nCode">후크 프로시저로 후크 코드가 전달된다.</param>
		/// <param name="wParam">후크 프로시저로 wParam가 전달된다.</param>
		/// <param name="lParam">후크 프로시저로 lParam가 전달된다.</param>
		/// <returns>후크 체인 내의 다음 후크 프로시저에 의해 반환된다. 또한, 현재 후크 프로시져는 이 값을 반환해야만 한다.</returns>
		[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
		public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, int wParam, IntPtr lParam);

		/// <summary>
		/// <para>후크 체인에 어플리케이션 정의 후크 프로시져를 설치한다. 시스템을 모니터링하기 위해서 명확한 유형의 이벤트를 위한 후크 프로시져를 설치해야만 한다.</para>
		/// <para>이 이벤트들은 각각 호출되는 스레드와 동일한 데스크톱 안에서 특정 스레드 혹은 모든 스레드와 연관되어 있어야 한다.</para>
		/// </summary>
		/// <param name="idHook">설치할 후크 프로시저의 유형을 설정한다.</param>
		/// <param name="lpfn">
		/// <para>후크 프로시저의 대리자를 설정한다. dwThreadId 파라메터가 0이거나 다른 프로세스에 의해 생성된 스레드의 고유 아이디를 가르키는 경우, DLL 내의 후크 프로시저를 가르켜야 한다.</para>
		/// <para>반면, 현재 프로세스와 연관된 코드 내의 후크 프로세스를 가르킬 수도 있다.</para>
		/// </param>
		/// <param name="hMod">
		/// <para>lpfn 파라메터에 의해 지정된 후크 프로시저를 포함하는 DLL의 핸들을 설정한다.</para>
		/// <para>dwThreadId 파라메터가 현재 프로세스에 의해 생성된 스레드를 지정하거나 후크 프로시저가 현재 프로세스와 연관된 코드에 포함되어 있는 경우 hMod 파라메터는 반드시 null되어야 한다.</para>
		/// </param>
		/// <param name="dwThreadId">후크 프로시저와 연관된 스레드의 아이디를 설정한다. 데스크톱 애플리케이션의 경우, 이 파라메터가 0이면 후크 프로시저는 호출 스레드에 대한 동일한 데스크톱에서 실행 중인 모든 스레드와 연관되어 있다.</param>
		/// <returns>함수가 성공할 경우, 반환값은 후크 프로시져의 핸들이 반환된다. 그렇지 않을 경우, 0이 반환된다.</returns>
		[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
		public static extern IntPtr SetWindowsHookEx(WindowHookId idHook, HookProc lpfn, IntPtr hMod, int dwThreadId);

		/// <summary>
		/// <para><see cref="Skying.Windows.API.User32.SetWindowsHookEx"/> 함수를 통해 설치되는 후크 프로시저 대리자를 나타낸다.</para>
		/// <para>각기 특수한 용도의 후크 유형들에 대한 후크 메시지를 받기 위해서는 후크 프로시저를 제공해야만 한다.</para>
		/// </summary>
		/// <param name="nCode">후크 프로시저가 실행할 액션을 결정하기 위해 사용되는 후크 코드를 나타낸다. 후크 코드의 값은 후크 코드가 갖는 특성에 따른다.</param>
		/// <param name="wParam">후크 코드에 따른 특유의 wParam이 전달된다.</param>
		/// <param name="lParam">후크 코드에 따른 특유의 lParam이 전달된다.</param>
		/// <returns>후크 프로시저의 핸들이 반환된다.</returns>
		public delegate IntPtr HookProc(int nCode, int wParam, IntPtr lParam);

		/// <summary>
		/// <para>시스템의 메시지 핸들링 메커니즘에 의거한 후크 유형을 열거한다. 어플리케이션으로 하여금 시스템 메시지를 모니터링할 수 있도록 도와준다.</para>
		/// <para>자세한 내용은 http://msdn.microsoft.com/ko-kr/library/windows/desktop/ms644959(v=vs.85).aspx#types 을 참조하기 바란다.</para>
		/// </summary>
		public enum WindowHookId
		{
			WH_CALLWNDPROC = 4,
			WH_CALLWNDPROCRET = 12,
			WH_CBT = 5,
			WH_DEBUG = 9,
			WH_FOREGROUNDIDLE = 11,
			WH_GETMESSAGE = 3,
			WH_JOURNALPLAYBACK = 1,
			WH_JOURNALRECORD = 0,
			WH_KEYBOARD = 2,
			WH_KEYBOARD_LL = 13,
			WH_MOUSE = 7,
			WH_MOUSE_LL = 14,
			WH_MSGFILTER = -1,
			WH_SHELL = 10,
			WH_SYSMSGFILTER = 6
		}

		/// <summary>
		/// <see cref="Skying.Windows.API.User32.SetWindowsHookEx"/> 함수에 의해 후크 체인 내에 설치된 후크 프로시저를 제거한다.
		/// </summary>
		/// <param name="hhk">제거할 후크의 핸들을 설정한다.</param>
		/// <returns>함수가 성공할 경우 true, 그렇지 않으면 false가 반환된다.</returns>
		[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
		public static extern bool UnhookWindowsHookEx(IntPtr hhk);

		/// <summary>
		/// 로우 레벨 마우스 입력 이벤트에 관한 정보를 포함하는 구조체를 정의한다.
		/// </summary>
		[StructLayout(LayoutKind.Sequential)]
		public struct MouseLLHookStruct
		{
			/// <summary>
			/// 스크린 좌표 영역 내에서 커서의 X, Y 좌표를 나타낸다.
			/// </summary>
			public Windef.Point Point;

			/// <summary>
			/// <para>마우스 이벤트가 <see cref="Skying.Windows.API.MouseInputNotifications.WM_MOUSEWHEEL"/>일 경우, MouseData의 high-order word는 휠의 회전수를 나타낸다.
			/// low-order word는 예약되어 있다. 양수 값은 휠이 사용자의 반대 방향으로 회전한 것을 나타내며, 음수 값인 경우 휠이 사용자를 향하도록 회전하는 것을 나타낸다.</para>
			/// <para>마우스 이벤트가 <see cref="Skying.Windows.API.MouseInputNotifications.WM_XBUTTONDOWN"/>, <see cref="Skying.Windows.API.MouseInputNotifications.WM_XBUTTONUP"/>, <see cref="Skying.Windows.API.MouseInputNotifications.WM_XBUTTONDBLCLK"/>, <see cref="Skying.Windows.API.MouseInputNotifications.WM_NCXBUTTONDOWN"/>, <see cref="Skying.Windows.API.MouseInputNotifications.WM_NCXBUTTONUP"/>, 혹은 <see cref="Skying.Windows.API.MouseInputNotifications.WM_NCXBUTTONDBLCLK"/>인 경우,
			/// high-order word은 X 버튼이 눌렸는지 아니면 떼어졌는지를 나타낸다. 그리고 low-order word는 예약되어 있다. 이 값은 첫 번째 X 버튼(XBUTTON1)일 경우 0x0001, 두 번째 X 버튼(XBUTTON2)일 경우 0x0002이다.</para>
			/// </summary>
			public int MouseData;

			/// <summary>
			/// event-injected 플래그를 설정할 수 있다. 마우스 플래그들을 테스트하기 위해 LLMHF_INJECTED(0x00000001) 값을 사용할 수 있다.
			/// </summary>
			public int Flags;

			/// <summary>
			/// 이벤트에 대한 타임 스탬프를 나타낸다.
			/// </summary>
			public int Time;

			/// <summary>
			/// 이벤트와 관련된 추가적인 정보를 나타낸다.
			/// </summary>
			public int ExtraInfo;
		}

		/// <summary>
		/// 로우 레벨 키보드 입력 이벤트에 대한 정보를 포함하는 구조체를 정의한다.
		/// </summary>
		[StructLayout(LayoutKind.Sequential)]
		public struct KbdLLHookStruct
		{
			/// <summary>
			/// Virtual-key 코드를 나타낸다. 이 코드는 반드시 1부터 254 사이의 값이어야 한다.
			/// </summary>
			public int VirtualKeyCode;

			/// <summary>
			/// 키에 대한 하드웨어 스캔 코드를 나타낸다.
			/// </summary>
			public int ScanCode;

			/// <summary>
			/// extended-key 플래그, event-injected 플래그, 컨텍스트 코드, 그리고 transition-state 플래그를 나타낸다.
			/// </summary>
			public int Flags;

			/// <summary>
			/// 이벤트에 대한 타임 스탬프를 나타낸다.
			/// </summary>
			public int Time;

			/// <summary>
			/// 이벤트와 관련된 추가 정보를 나타낸다.
			/// </summary>
			public int ExtraInfo;
		}

		#endregion

		#region Keyboard Input Functions

		/// <summary>
		/// 256개의 virtual key들의 상태를 버퍼에 복사한다.
		/// </summary>
		/// <param name="pbKeyState">각 virtual key들에 대한 상태 정보를 담는 버퍼를 참조한다.</param>
		/// <returns>함수가 성공하면 true, 그렇지 않으면 false가 반환된다.</returns>
		[DllImport("user32", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
		public static extern bool GetKeyboardState(out byte[] lpKeyState);

		/// <summary>
		/// 특정 virtual key의 상태를 검사한다. 검사한 상태는 키가 떼어졌는지, 눌렸는지 혹은 토글되었는지를 나타낸다.
		/// </summary>
		/// <param name="nVirtKey">검사할 <see cref="Skying.Windows.API.VirtualKeyCodes"/>를 나타낸다. 요구된 virtual key가 문자 혹은 숫자일 경우, 이 파라메터는 반드시 ASCII 코드 캐릭터로 설정되어야 한다.</param>
		/// <returns>
		/// <para>high-order 비트가 1인 경우, 키가 눌린 것을 나타낸다. 그렇지 않은 경우는 떼어진 것이다.</para>
		/// <para>low-order 비트가 1인 경우, 키가 토글된 것을 나타낸다. CAPS LOCK과 같은 키의 경우 토글되어 켜진 것을 말한다. 반대로 0인 경우에는 키가 꺼진 것을 말한다.</para>
		/// </returns>
		[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
		public static extern short GetKeyState(Winuser.VirtualKeyCodes nVirtKey);

		/// <summary>
		/// 특정 virtual-key 코드와 키 상태를 그에 상응하는 문자(들)로 변환한다. 이 함수는 입력 언어와 키보드 레이아웃 핸들에 의해 구분되는 물리적인 키보드 레이아웃을 이용해 코드를 변환한다.
		/// </summary>
		/// <param name="uVirtKey">변환하려는 <see cref="Skying.Windows.API.VirtualKeyCodes"/>를 설정한다.</param>
		/// <param name="uScanCode">변환하려는 키의 하드웨어 스캔 코드를 설정한다.</param>
		/// <param name="lpKeyState">
		/// <para>현재 키보드 상태를 나타내는 256 자리 바이트 배열을 설정한다. 바이트 배열의 각 배열은 하나의 키에 대한 상태를 나타낸다. high-order 비트가 세트되어 있으면, 키가 눌린 것을 나타낸다.</para>
		/// <para>low-order 비트가 세트되어 있으면 키가 토글된 것을 나타낸다.</para>
		/// </param>
		/// <param name="lpChar">변환된 문자(들)이 저장되는 버퍼를 참조한다.</param>
		/// <param name="uFlags"></param>
		/// <returns></returns>
		[DllImport("user32", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
		public static extern int ToAscii(Winuser.VirtualKeyCodes uVirtKey, int uScanCode, byte[] lpKeyState, out byte[] lpChar, int uFlags);

		#endregion
	}
}